home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 19 / Mac Magazin and MacEasy Magazine CD - Issue 19.iso / Wissenschaft & Technik / WASTE 1.2a5 Distribution / WASTE 1.2a5 / WEInlineInput.c < prev    next >
Text File  |  1996-01-13  |  16KB  |  628 lines

  1. /*
  2.  *    WEInlineInput.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Inline Input Support
  6.  *
  7.  *  Copyright (c) 1993-1996 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. // static variables
  18.  
  19. static AEEventHandlerUPP _weUpdateActiveInputAreaHandler = NULL;
  20. static AEEventHandlerUPP _wePositionToOffsetHandler = NULL;
  21. static AEEventHandlerUPP _weOffsetToPositionHandler = NULL;
  22.  
  23. pascal OSErr _WEHiliteRangeArray(TextRangeArrayHandle hTray, WEHandle hWE)
  24. {
  25.     WEPtr pWE = *hWE;    // assume WE record is already locked
  26.     TextRangePtr pRange;
  27.     long rangeStart, rangeEnd;
  28.     short hiliteStyle;
  29.     WETextStyle ts;
  30.     short rangeIndex;
  31.     Boolean saveTrayLock;
  32.     OSErr err;
  33.  
  34.     // lock down the range array
  35.     saveTrayLock = _WESetHandleLock((Handle)hTray, true);
  36.     pRange = (*hTray)->fRange;
  37.  
  38.     // walk the hilite range array
  39.     for (rangeIndex = (*hTray)->fNumOfRanges - 1; rangeIndex >= 0; rangeIndex-- )
  40.     {
  41.  
  42.         // the offsets in the range array are relative to the beginning
  43.         // of the active input area: convert them to absolute offsets
  44.         rangeStart = pWE->tsmAreaStart + pRange->fStart;
  45.         rangeEnd = pWE->tsmAreaStart + pRange->fEnd;
  46.         hiliteStyle = pRange->fHiliteStyle;
  47.  
  48.         // take the absolute value of hiliteStyle
  49.         hiliteStyle = ABS(hiliteStyle);
  50.  
  51.         // if hiliteStyle is kCaretPosition, set the selection range
  52.         if (hiliteStyle == kCaretPosition)
  53.         {
  54.             pWE->selStart = rangeStart;
  55.             pWE->selEnd = rangeEnd;
  56.         }
  57.         else
  58.         {
  59.             hiliteStyle -= kRawText;
  60.             // otherwise set the WETextStyle flags of the specified range appropriately
  61.             if ((hiliteStyle >= 0) && (hiliteStyle <= 3))
  62.             {
  63.                 ts.tsFlags = 0x10 + (hiliteStyle << tsTSMSelected);
  64.                 if ((err = _WESetStyleRange(rangeStart, rangeEnd, weDoFlags, &ts, hWE)) != noErr)
  65.                 {
  66.                     goto cleanup;
  67.                 }
  68.             }
  69.         }        
  70.         // go to next text range element
  71.         pRange++;
  72.     }
  73.  
  74.     // clear result code
  75.     err = noErr;
  76.  
  77. cleanup:
  78.     // unlock the range array
  79.     _WESetHandleLock((Handle)hTray, saveTrayLock);
  80.  
  81.     return err;
  82. }
  83.  
  84. pascal OSErr _WEHandleUpdateActiveInputArea(const AppleEvent *ae, AppleEvent *reply, long handlerRefCon)
  85. {
  86. #pragma unused(reply, handlerRefCon)
  87.     WEHandle hWE;
  88.     WEPtr pWE;
  89.     AEDesc text;
  90.     AEDesc hiliteTray;
  91.     TextRange pinRange;
  92.     long totalLength;
  93.     long fixLength;
  94.     long tsmOffset;
  95.     DescType returnedType;
  96.     long actualSize;
  97.     GrafPtr savePort;
  98.     WEActionHandle hAction;
  99.     Boolean saveAutoScroll;
  100.     Boolean saveTextLock;
  101.     Boolean saveWELock;
  102.     OSErr err;
  103.  
  104.     hWE = NULL;
  105.  
  106.     // initialize descriptors to null values
  107.     text.descriptorType = typeNull;
  108.     text.dataHandle = NULL;
  109.     hiliteTray.descriptorType = typeNull;
  110.     hiliteTray.dataHandle = NULL;
  111.  
  112.     // extract WE handle
  113.     if ((err = AEGetParamPtr(ae, keyAETSMDocumentRefcon, typeLongInteger,
  114.             &returnedType, &hWE, sizeof(hWE), &actualSize)) != noErr)
  115.     {
  116.         goto cleanup;
  117.     }
  118.  
  119.     // lock the WE handle
  120.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  121.     pWE = *hWE;
  122.  
  123.     // return an error code if this instance is read-only
  124.     err = weReadOnlyErr;
  125.     if (BTST(pWE->features, weFReadOnly))
  126.         goto cleanup;
  127.  
  128.     // call the pre-update callback, if present
  129.     if (pWE->tsmPreUpdate != NULL)
  130.     {
  131.         CallWETSMPreUpdateProc(hWE, pWE->tsmPreUpdate);
  132.     }
  133.  
  134.     // hide the caret if it's showing
  135.     if (BTST(pWE->flags, weFCaretVisible))
  136.     {
  137.         _WEBlinkCaret(hWE);
  138.     }
  139.     
  140.     // extract the text descriptor
  141.     if ((err = AEGetParamDesc(ae, keyAETheData, typeChar, &text)) != noErr)
  142.     {
  143.         goto cleanup;
  144.     }
  145.     
  146.     // get total length of text in the active input area
  147.     totalLength = GetHandleSize(text.dataHandle);
  148.  
  149.     // extract the length of confirmed text in the active input area
  150.     if ((err = AEGetParamPtr(ae, keyAEFixLength, typeLongInteger, &returnedType,
  151.             &fixLength, sizeof(fixLength), &actualSize)) != noErr)
  152.     {
  153.         goto cleanup;
  154.     }
  155.  
  156.     // if fixLength = -1, all text is confirmed
  157.     if (fixLength == -1)
  158.     {
  159.         fixLength = totalLength;
  160.     }
  161.  
  162.     // if there's currently no active input area, open one
  163.     if (pWE->tsmAreaStart == kInvalidOffset)
  164.     {
  165.         pWE->tsmAreaStart = pWE->selStart;
  166.         pWE->tsmAreaEnd = pWE->selEnd;
  167.         
  168.         // are we tracking a typing sequence?
  169.         if (!WEIsTyping(hWE))
  170.         {
  171.             // nope; so start a new one
  172.             // increment modification count
  173.             pWE->modCount++;
  174.  
  175.             // if undo support is enabled, the inline session just started may initiate
  176.             if (BTST(pWE->features, weFUndoSupport))
  177.             {
  178.                 WEClearUndo(hWE);
  179.                 if (_WENewAction(pWE->selStart, pWE->selEnd, 0, weAKTyping, 0, hWE, &hAction) == noErr)
  180.                 {
  181.                     _WEPushAction(hAction);
  182.                 }
  183.             }
  184.         }
  185.     }
  186.  
  187.     tsmOffset = pWE->tsmAreaStart;
  188.  
  189.     // the new text replaces whatever is in the active input area
  190.     if ((err = _WEDeleteRange(tsmOffset, pWE->tsmAreaEnd, hWE)) != noErr)
  191.     {
  192.         goto cleanup;
  193.     }
  194.  
  195.     // synchronize the null style, so font script matches the keyboard script
  196.     _WESynchNullStyle(hWE);
  197.  
  198.     // set the port font for good measure
  199.     GetPort(&savePort);
  200.     SetPort(pWE->port);
  201.     TextFont(pWE->nullStyle.runStyle.tsFont);
  202.     SetPort(savePort);
  203.  
  204.     // temporarily lock the text
  205.     saveTextLock = _WESetHandleLock(text.dataHandle, true);
  206.  
  207.     // insert the text
  208.     if ((err = _WEInsertText(tsmOffset, *(text.dataHandle), totalLength, hWE)) != noErr)
  209.     {
  210.         goto cleanup;
  211.     }
  212.  
  213.     // unlock the text
  214.     _WESetHandleLock(text.dataHandle, saveTextLock);
  215.  
  216.     // extract pin range
  217.     if ((err = AEGetParamPtr(ae, keyAEPinRange, typeTextRange, &returnedType,
  218.             &pinRange, sizeof(pinRange), &actualSize)) == noErr)
  219.     {
  220.         // we want absolute offsets
  221.         pinRange.fStart += tsmOffset;
  222.         pinRange.fEnd += tsmOffset;
  223.     }
  224.     else
  225.     {
  226.         // a missing pin range descriptor isn't an error; everything else is
  227.         if (err != errAEDescNotFound)
  228.         {
  229.             goto cleanup;
  230.         }
  231.  
  232.         // default pin range is active input area
  233.         pinRange.fStart = tsmOffset;
  234.         pinRange.fEnd = pWE->tsmAreaEnd;
  235.     }
  236.  
  237.     // NOTE: if fixLength == totalLength, the inline input session is over, so, in theory,
  238.     // hiliteTray should either be missing or not specify any range to be underlined.
  239.     // Unfortunately, some input methods (like Apple's input method for Simplified Chinese)
  240.     // do specify kConvertedText (= thin black underline) for the whole text when
  241.     // the text is confirmed (is this a bug?).  To work around this, we deliberately ignore
  242.     // the hiliteTray parameter when fixLength = totalLength.
  243.  
  244.     if (fixLength != totalLength)
  245.     {
  246.         pWE->tsmAreaStart += fixLength;  // added by Kiyoshi Gomasaki for ATOK compatibility
  247.  
  248.         // extract the highlight range array
  249.         if ((err = AEGetParamDesc(ae, keyAEHiliteRange, typeTextRangeArray, &hiliteTray)) != noErr)
  250.         {
  251.             if (err != errAEDescNotFound)
  252.             {
  253.                 goto cleanup;
  254.             }
  255.         }
  256.     }
  257.  
  258.     if (hiliteTray.dataHandle != NULL)
  259.     {
  260.         if ((err = _WEHiliteRangeArray((TextRangeArrayHandle) hiliteTray.dataHandle, hWE)) != noErr)
  261.         {
  262.             goto cleanup;
  263.         }
  264.     }
  265.     else
  266.     {
  267.         pWE->selStart = tsmOffset + fixLength;
  268.         pWE->selEnd = pWE->selStart;
  269.     }
  270.  
  271.     // temporarily disable auto-scroll, as we need to scroll manually according to pinRange
  272.     saveAutoScroll = BTST(pWE->features, weFAutoScroll) ? true : false;
  273.     BCLR(pWE->features, weFAutoScroll);
  274.     
  275.     // redraw the active input area
  276.     if ((err = _WERedraw(tsmOffset, tsmOffset + totalLength, hWE)) != noErr)
  277.     {
  278.         goto cleanup;
  279.     }
  280.  
  281.     if (saveAutoScroll)
  282.     {
  283.     
  284.         // re-enable auto-scroll
  285.         BSET(pWE->features, weFAutoScroll);
  286.  
  287.         // scroll the pin range into view
  288.         if (!_WEScrollIntoView(pinRange.fStart, hWE))
  289.         {
  290.             if (pinRange.fStart != pinRange.fEnd)
  291.             {
  292.                 _WEScrollIntoView(pinRange.fEnd, hWE);
  293.             }
  294.         }
  295.     }
  296.  
  297.     // update the boundaries of the active input area
  298.     // if fixLength = totalLength, the inline input session is over: close the active input area
  299.     if (fixLength == totalLength)
  300.     {
  301.         pWE->tsmAreaStart = kInvalidOffset;
  302.         pWE->tsmAreaEnd = kInvalidOffset;
  303.  
  304.         // adjust undo buffer (if any) for the confirmed text
  305.         _WEAdjustUndoRange(fixLength, hWE);
  306.     }
  307.     else
  308.     {
  309.         // otherwise, fixLength defines the boundaries of the active input area
  310.         pWE->tsmAreaStart = tsmOffset + fixLength;
  311.         pWE->tsmAreaEnd = tsmOffset + totalLength;
  312.     }
  313.  
  314.     // call the post-update callback, if present
  315.     if (pWE->tsmPostUpdate != NULL)
  316.     {
  317.         CallWETSMPostUpdateProc(hWE, fixLength, pWE->tsmAreaStart, pWE->tsmAreaEnd,
  318.             pinRange.fStart, pinRange.fEnd, pWE->tsmPostUpdate);
  319.     }
  320.  
  321.     // clear result code
  322.     err = noErr;
  323.  
  324. cleanup:
  325.     // clean up
  326.     AEDisposeDesc(&text);
  327.     AEDisposeDesc(&hiliteTray);
  328.  
  329.     // unlock the WE record
  330.     if (hWE !=NULL)
  331.     {
  332.         _WESetHandleLock((Handle) hWE, saveWELock);
  333.     }
  334.  
  335.     // return result code
  336.     return err;
  337. }
  338.  
  339.  
  340. pascal OSErr _WEHandlePositionToOffset(const AppleEvent *ae, AppleEvent *reply, long handlerRefCon)
  341. {
  342. #pragma unused(handlerRefCon)
  343.     WEHandle hWE;
  344.     WEPtr pWE;
  345.     Point position;
  346.     LongPt thePoint;
  347.     short regionClass;
  348.     long offset;
  349.     DescType returnedType;
  350.     long actualSize;
  351.     GrafPtr savePort;
  352.     char edge;
  353.     Boolean saveWELock;
  354.     OSErr err;
  355.  
  356.     hWE = NULL;
  357.  
  358.     // extract WE handle
  359.     if ((err = AEGetParamPtr(ae, keyAETSMDocumentRefcon, typeLongInteger,
  360.             &returnedType, &hWE, sizeof(hWE), &actualSize)) != noErr)
  361.     {
  362.         goto cleanup;
  363.     }
  364.  
  365.     // lock the WE record
  366.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  367.     pWE = *hWE;
  368.  
  369.     // extract position parameter
  370.     if ((err = AEGetParamPtr(ae, keyAECurrentPoint, typeQDPoint, &returnedType, &position, sizeof(position), &actualSize)) != noErr)
  371.     {
  372.             goto cleanup;
  373.     }
  374.  
  375.     // convert position to local...
  376.     GetPort(&savePort);
  377.     SetPort(pWE->port);
  378.     GlobalToLocal(&position);
  379.     SetPort(savePort);
  380.  
  381.     // ...and long coordinates
  382.     WEPointToLongPoint(position, &thePoint);
  383.  
  384.     // find the byte offset and the edge value corresponding to the given position
  385.     offset = WEGetOffset(&thePoint, &edge, hWE);
  386.  
  387.     // determine the region class
  388.     if (WELongPointInLongRect(&thePoint, &pWE->viewRect))
  389.     {
  390.         if (_WEOffsetInRange(offset, edge, pWE->tsmAreaStart, pWE->tsmAreaEnd))
  391.         {
  392.               regionClass = kTSMInsideOfActiveInputArea;
  393.             // if the given position is within the active input area, we're supposed to return
  394.             // an offset relative to the beginning of this area (thanks, Martin!)
  395.             offset -= pWE->tsmAreaStart;
  396.         }
  397.         else
  398.         {
  399.             // otherwise the offset is relative to the beginning of the body
  400.             regionClass = kTSMInsideOfBody;
  401.         }
  402.     }
  403.     else
  404.     {
  405.         regionClass = kTSMOutsideOfBody;
  406.     }
  407.     
  408.     // add region class parameter to reply
  409.     if ((err = AEPutParamPtr(reply, keyAERegionClass, typeShortInteger, ®ionClass, sizeof(regionClass))) != noErr)
  410.     {
  411.         goto cleanup;
  412.     }
  413.     
  414.     // add offset parameter to reply
  415.     if ((err = AEPutParamPtr(reply, keyAEOffset, typeLongInteger, &offset, sizeof(offset))) != noErr) 
  416.     {
  417.         goto cleanup;
  418.     }
  419.     
  420.     // add edge parameter to reply
  421.     if ((err = AEPutParamPtr(reply, keyAELeftSide, typeBoolean, &edge, sizeof(edge))) != noErr)
  422.     {
  423.         goto cleanup;
  424.     }
  425.  
  426.     // clear result code
  427.     err = noErr;
  428.  
  429. cleanup:
  430.     // unlock the WE record
  431.     if (hWE != NULL)
  432.     {
  433.         _WESetHandleLock((Handle) hWE, saveWELock);
  434.     }
  435.     
  436.     // return result code
  437.     return err;
  438. }
  439.  
  440. pascal OSErr _WEHandleOffsetToPosition(const AppleEvent *ae, AppleEvent *reply, long handlerRefCon)
  441. {
  442. #pragma unused(handlerRefCon)
  443.     WEHandle hWE;
  444.     WEPtr pWE;
  445.     long offset;
  446.     LongPt thePoint;
  447.     Point position;
  448.     short lineHeight;
  449.     DescType returnedType;
  450.     long actualSize;
  451.     GrafPtr savePort;
  452.     Boolean saveWELock;
  453.     OSErr err;
  454.  
  455.     hWE = NULL;
  456.  
  457.     // extract WE handle
  458.     if ((err = AEGetParamPtr(ae, keyAETSMDocumentRefcon, typeLongInteger,
  459.             &returnedType, &hWE, sizeof(hWE), &actualSize)) != noErr)
  460.     {
  461.         goto cleanup;
  462.     }
  463.  
  464.     // lock the WE record
  465.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  466.     pWE = *hWE;
  467.  
  468.     // if there's no active input area, return errOffsetInvalid
  469.     err = errOffsetInvalid;
  470.     if (pWE->tsmAreaStart < 0)
  471.     {
  472.         goto cleanup;
  473.     }
  474.     
  475.     // extract the offset parameter
  476.     if ((err = AEGetParamPtr(ae, keyAEOffset, typeLongInteger, &returnedType, &offset, sizeof(offset), &actualSize)) != noErr)
  477.     {
  478.         goto cleanup;
  479.     }
  480.  
  481.     // offset is relative to the beginning of the active input area; we want an absolute offset
  482.     offset += pWE->tsmAreaStart;
  483.  
  484.     // make sure the offset is within the input area
  485.     if ((offset < pWE->tsmAreaStart) || (offset >= pWE->tsmAreaEnd))
  486.     {
  487.         err = errOffsetInvalid;
  488.         goto cleanup;
  489.     }
  490.  
  491.     // find the position corresponding to the given offset (in long coordinates)
  492.     WEGetPoint(offset, &thePoint, &lineHeight, hWE);
  493.     thePoint.v += lineHeight;
  494.  
  495.     // make sure offset is within view rectangle
  496.     if (!WELongPointInLongRect(&thePoint, &pWE->viewRect))
  497.     {
  498.         err = errOffsetIsOutsideOfView;
  499.         goto cleanup;
  500.     }
  501.  
  502.     // convert the point to short...
  503.     WELongPointToPoint(&thePoint, &position);
  504.  
  505.     // ...and global coordinates
  506.     GetPort(&savePort);
  507.     SetPort(pWE->port);
  508.     LocalToGlobal(&position);
  509.     SetPort(savePort);
  510.  
  511.     // add keyAEPoint parameter to the reply Apple event
  512.     if ((err = AEPutParamPtr(reply, keyAEPoint, typeQDPoint,
  513.             &position, sizeof(position))) != noErr)
  514.     {
  515.         goto cleanup;
  516.     }
  517.  
  518.     // add keyAETSMTextFont parameter to the reply Apple event
  519.     if ((err = AEPutParamPtr(reply, keyAETSMTextFont, typeShortInteger,
  520.             &pWE->nullStyle.runStyle.tsFont, sizeof(pWE->nullStyle.runStyle.tsFont))) != noErr)
  521.     {
  522.         goto cleanup;
  523.     }
  524.     
  525.     // add keyAETSMTextPointSize parameter to the reply Apple event
  526.     if ((err = AEPutParamPtr(reply, keyAETSMTextPointSize, typeShortInteger,
  527.             &pWE->nullStyle.runStyle.tsSize, sizeof(pWE->nullStyle.runStyle.tsSize))) != noErr)
  528.     {
  529.         goto cleanup;
  530.     }
  531.  
  532.     // add keyAETextLineAscent parameter to the reply Apple event
  533.     if ((err = AEPutParamPtr(reply, keyAETextLineAscent, typeShortInteger,
  534.             &pWE->nullStyle.runAscent, sizeof(pWE->nullStyle.runAscent))) != noErr)
  535.     {
  536.         goto cleanup;
  537.     }
  538.     
  539.     // add keyAETextLineHeight parameter to the reply Apple event
  540.     if ((err = AEPutParamPtr(reply, keyAETextLineHeight, typeShortInteger,
  541.             &pWE->nullStyle.runHeight, sizeof(pWE->nullStyle.runHeight))) != noErr)
  542.     {
  543.         goto cleanup;
  544.     }
  545.     
  546.     // clear result code
  547.     err = noErr;
  548.  
  549. cleanup:
  550.     // unlock the WE record
  551.     if (hWE != NULL)
  552.     {
  553.         _WESetHandleLock((Handle) hWE, saveWELock);
  554.     }
  555.     
  556.     // return result code
  557.     return err;
  558. }
  559.  
  560. pascal OSErr WEInstallTSMHandlers(void)
  561. {
  562.     OSErr err;
  563.     
  564.     // the first time we're called, create routine descriptors for our Apple event handlers
  565.     if (_weUpdateActiveInputAreaHandler == NULL)
  566.     {
  567.         _weUpdateActiveInputAreaHandler = NewAEEventHandlerProc(_WEHandleUpdateActiveInputArea);
  568.         _wePositionToOffsetHandler = NewAEEventHandlerProc(_WEHandlePositionToOffset);
  569.         _weOffsetToPositionHandler = NewAEEventHandlerProc(_WEHandleOffsetToPosition);
  570.     }
  571.  
  572.     // install Apple Event handlers to be used by Text Service components
  573.     if ((err = AEInstallEventHandler(kTextServiceClass, kUpdateActiveInputArea, _weUpdateActiveInputAreaHandler, 0, false)) != noErr)
  574.     {
  575.         goto cleanup;
  576.     }
  577.         
  578.     if ((err = AEInstallEventHandler(kTextServiceClass, kPos2Offset, _wePositionToOffsetHandler, 0, false)) != noErr)
  579.     {
  580.         goto cleanup;
  581.     }
  582.     
  583.     if ((err = AEInstallEventHandler(kTextServiceClass, kOffset2Pos, _weOffsetToPositionHandler, 0, false)) != noErr)
  584.     {
  585.         goto cleanup;
  586.     }
  587.     
  588.     // clear result code
  589.     err = noErr;
  590.  
  591. cleanup:
  592.     // return result code
  593.     return err;
  594. }
  595.  
  596. pascal OSErr WERemoveTSMHandlers(void)
  597. {
  598.     OSErr err;
  599.     
  600.     // return an error code if WEInstallTSMHandlers has never been called
  601.     if (_weUpdateActiveInputAreaHandler == NULL)
  602.         return errAEHandlerNotFound;
  603.  
  604.     // remove the handlers
  605.     if ((err = AERemoveEventHandler(kTextServiceClass, kUpdateActiveInputArea, _weUpdateActiveInputAreaHandler, false)) != noErr)
  606.     {
  607.         goto cleanup;
  608.     }
  609.         
  610.     if ((err = AERemoveEventHandler(kTextServiceClass, kPos2Offset, _wePositionToOffsetHandler, false)) != noErr)
  611.     {
  612.         goto cleanup;
  613.     }
  614.     
  615.     if ((err = AERemoveEventHandler(kTextServiceClass, kOffset2Pos, _weOffsetToPositionHandler, false)) != noErr)
  616.     {
  617.         goto cleanup;
  618.     }
  619.     
  620.     // clear result code
  621.     err = noErr;
  622.     
  623. cleanup:
  624.     // clear result code
  625.     return err;
  626.  
  627. } // WERemoveTSMHandlers
  628.